package v9.assist.analyst

import android.graphics.Bitmap
import android.graphics.Rect
import sample.ui.Properties
import sample.ui.DescLayout.Companion.runTime

import abc.reflect.App
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.script.Block
import kotlinx.coroutines.script.C
import kotlinx.coroutines.script.CComponents
import kotlinx.coroutines.script.Controller
import kotlinx.coroutines.script.GComponents
import kotlinx.coroutines.script.analysis.Analysis
import kotlinx.coroutines.script.analysis.AnalysisInvite
import kotlinx.coroutines.script.analysis.HitColors
import kotlinx.coroutines.script.tes.ColorDiffer
import kotlinx.coroutines.script.tes.Keys
import kotlinx.coroutines.script.tes.analysisEvent
import kotlinx.coroutines.script.tes.appDeviceId
import kotlinx.coroutines.script.tes.appScopeLaunch
import kotlinx.coroutines.script.tes.containsSimilarColor
import kotlinx.coroutines.script.tes.phoneDefaultDisplayPoint
import kotlinx.coroutines.script.tes.phoneDensityDpi
import kotlinx.coroutines.script.tes.phoneScreenHeight
import kotlinx.coroutines.script.tes.phoneScreenWidth
import kotlinx.coroutines.script.tes.printLog
import kotlinx.coroutines.script.tes.pt
import kotlinx.coroutines.script.tes.readByDisk
import kotlinx.coroutines.script.tes.rgb
import kotlinx.coroutines.script.tes.shake
import kotlinx.coroutines.script.tes.writeToDisk
import v9.assist.access.accLastInvokedTs
import v9.assist.access.btnSceneStartEnter
import v9.assist.access.continueCounter
import v9.assist.script.SCRIPT_MAIN
import v9.assist.script.xAtkByBossBesideTs
import v9.assist.script.xBossBesideTs
import v9.assist.script.xLastAtkTs
import v9.assist.script.xLastSceneEnterTs
import v9.assist.script.xLastUnknownTs
import kotlin.math.max
import kotlin.math.min

const val MODE_PATHFINDING_P1 = "p1"
const val MODE_PATHFINDING_P2 = "p2"
const val MODE_PATHFINDING_P3 = "p3"
const val MODE_PATHFINDING_P4 = "p4"
const val MODE_PATHFINDING_P5 = "p5"
const val MODE_PATHFINDING_P6 = "p6"
const val PAGE_INITIAL = "initial"
const val PAGE_MAIN = "main"
const val PAGE_SCENE_ENTER = "sceneEnter"
const val PAGE_REVIVE = "revive"
const val PAGE_BE_INVITED = "beInvited"
const val PAGE_SKIP = "skip"
const val PAGE_BASE_MENU = "baseMenu"
const val PAGE_TALK_BLACKSMITH = "talkBlacksmith"
const val PAGE_TALK_NPC = "talkNpc"
const val PAGE_SCENE_COMPLETED = "sceneCompleted"
const val PAGE_SCENE_GROUP_INVITE_PLAYER = "sceneGroupInviteAll"
const val PAGE_APPLY = "apply"
const val PAGE_APPLY_EMPTY = "applyEmpty"
const val PAGE_CLOSEABLE = "closeable"
const val PAGE_POP_TEXT = "popText"
const val PAGE_BOSS_DIRECT_OR_BUFF = "bossDirectOrBuff" // maybe buff btn
const val PAGE_REVIVE_BY_OTHER = "reviveByOther"
const val PAGE_BE_INVITED_BLACK_GROUP = "beInvitedBlackGroup" // 极低优先级
const val PAGE_ATV = "atv" // 六星熔铸 极低优先级 容易误判
const val PAGE_EDIT_FRIEND_NAME = "editFriendName" // 编辑好友备注 极低优先级
const val PAGE_GBWJ_COMPLETED = "gbwjCompleted"
const val PAGE_GBWJ_REVIVE = "gbwjRevive"
const val PAGE_SELECT_ROLE = "selectRole"
const val PAGE_ROOT_ENTER = "rootEnter"
const val PAGE_ROOT_ENTER_TIP = "rootEnterTip"
const val PAGE_SCENE_ENTER_IMM = "sceneEnterImm"
const val PAGE_SCENE_ENTER_TZ = "sceneEnterTz"
//const val PAGE_IMM_BUFF = "immBuff"
const val PAGE_IMM_DAILY_SIGN = "immDailySign"
const val PAGE_IMM_KEY = "immKey" // 放钥匙界面
const val PAGE_LX_REVIVE = "lxRevive"
const val PAGE_LX_USE_CONFIRM = "lxUseConfirm"
const val PAGE_MYMZ_START = "mymzStart"
const val PAGE_MYBL_FAIL_EXIT = "myblFailExit"
const val PAGE_MYBL_FINISH = "myblFinish"

var analystInitialized = false
private var lastEnemyCacheTs = 0L
private var lastEnemyCache: String? = null
private var excludeEnemyName: String? = null
fun anaUpdateExcludeEnemyName(name: String) {
    excludeEnemyName = name
}
var anaCounterNoSkill = 0
var anaCounterSceneSettle = 0
var anaLastSceneSettleTs = 0L
var bsSceneSettleBlacksmith = false
var bsSceneSettle = false
var bsSceneSettleBlacksmithMove = 0
var bsSceneSettleBlueDoorMove = 0
var beSceneSettleToBlacksmithByBlueDoor = 0 // 补救.没分级直接传送门的这种1次
fun anaClearSceneSettle() {
    anaCounterSceneSettle = 0
    //anaLastSceneSettleTs = 0L不用清理这个
    bsSceneSettle = false
    bsSceneSettleBlacksmith = false
    bsSceneSettleBlacksmithMove = 0
    bsSceneSettleBlueDoorMove = 0
    beSceneSettleToBlacksmithByBlueDoor = 0
}
fun anaMarkSceneSettle() {
    anaLastSceneSettleTs = System.currentTimeMillis()
    bsSceneSettle = true
    bsSceneSettleBlacksmith = true
    beSceneSettleToBlacksmithByBlueDoor = 1
}

var anaLastPlayerHpLosingTs = 0L
var anaLastHasInteractTs = 0L
var anaLastHasBackpack100Ts = 0L
var anaLastHasEnemyNameTs = 0L
var anaLastHasEnemyBossNameTs = 0L
var anaLastHasEliteEnemyNameTs = 0L
var anaLastTransmittingTs = 0L
var anaLastApplyDotTs = 0L
var anaLastPageNotMainTs = 0L
var anaLastPageSceneEnterTs = 0L
var anaLastPageReviveTs = 0L
var anaLastPageBeInvitedTs = 0L
var anaLastPageSceneGroupInviteAllTs = 0L
var anaLastPageSkipTs = 0L
var anaLastPageBaseMenuTs = 0L
var anaLastPageTalkBlacksmithTs = 0L
var anaLastPageTalkNpcTs = 0L
var anaLastPageMYStartTs = 0L
var anaLastPageSceneCompletedTs = 0L
var anaLastPageApplyTs = 0L
var anaLastPageApplyEmptyTs = 0L
var anaLastPageCloseableTs = 0L
var anaLastPagePopTextTs = 0L
var anaLastPageBossDirectOrBuffTs = 0L
var anaLastPageReviveByOtherTs = 0L
var anaLastPageBeInvitedBlackGroupTs = 0L
var anaLastPageAtvTs = 0L
var anaLastPageEditFriendCloseTs = 0L
var anaLastPageGBWJCompletedTs = 0L
var anaLastActionStoppedTs = 0L
var counterActionStopped = 0L
var anaLastMyBLLv3Ts = 0L
var anaLastPageMYBLCompletedTs = 0L
var anaLastLxLackOfMaterialsTs = 0L
private var bitmapLastScreenshot: Bitmap? = null
private var page: String = PAGE_INITIAL
private var playerHp: Float = 0f
private var playerHpLosing: Boolean = false
private var hasInteract: Boolean = false
private var hasTransmitting: Boolean = false
private var hasApplyDot: Boolean = false
private var hasReviveRightBtn: Boolean = false
private var acceptInvite: Boolean = false
private var acceptApply: Boolean = false
private var blockSceneEnter: Block? = null
private var blockCloseable: Block? = null
private var blockBlackGroup: Block? = null
private var blockImmEnter: Block? = null
private var blockTzEnter: Block? = null
private var myBossPlace: String? = null
private var hasBackpack100: Boolean = false
private var myBLLv3: Boolean = false
//private var lxCountEnable: Boolean = false
var rdRealScene: String? = null
var enemy: String? = null
var sceneRedStateTitle: Boolean = false
var lxInProgress: Boolean = false
var lxInProgressTs = 0L
var lxUseConfirmTs: Long = 0L
var blockMapMonsterEssence: Block? = null
var blockMapMonsterEssenceBtn : Block? = null
var anaLastMapMonsterEssenceTs = 0L
var npcAssistBlock: Block? = null
var inWorldPlace: Boolean = false
var rdInScene: Boolean = false
var rdFastAgain: Boolean = false

val bsPage get() = page
val bsPlayerHp get() = playerHp
val bsPlayerHpLosing get() = playerHpLosing
val bsHasInteract get() = hasInteract
val bsEnemy get() = enemy
val bsHasEnemy get() = enemy?.isNotEmpty() == true
val bsHasTransmitting get() = hasTransmitting
val bsHasApplyDot get() = hasApplyDot
val bsHasReviveRightBtn get() = hasReviveRightBtn
val bsAcceptInvite get() = acceptInvite
val bsBtnSceneEnter get() = blockSceneEnter
val bsAcceptApply get() = acceptApply
val bsBtnCloseable get() = blockCloseable
val bsMyBossPlace get() = myBossPlace
val bsBossBeside get() = System.currentTimeMillis() - xBossBesideTs < 1500 && System.currentTimeMillis() - xAtkByBossBesideTs > 4000

fun analysisScreenshot(ss: Bitmap?) {
    if (ss == null) return
    analystInitialized = true
    val maybePageMain = assertPageMain(ss)
    val currentPlayerHp = calcHpPercent(ss)
    playerHpLosing = (System.currentTimeMillis() - anaLastPageNotMainTs > 8000) && playerHp > 0.01f && currentPlayerHp < playerHp - 0.015
    if (playerHpLosing) anaLastPlayerHpLosingTs = System.currentTimeMillis()
    if (!playerHpLosing && System.currentTimeMillis() - anaLastPlayerHpLosingTs < 1000 && (System.currentTimeMillis() - anaLastPageNotMainTs > 8000)) playerHpLosing = true

    playerHp = currentPlayerHp
    var thatPage = PAGE_INITIAL

    if (!maybePageMain || playerHp <= 0.01f) { // 木筏被识别成主页面了
        val hasReviveWindow = // hasReviveLeft 0.49642858
            Analysis.hit(ss, CComponents.reviveInCity, HitColors.redButton2, 12.pt, 0.38f, ) // 0.6 "reviveInCity"
                    && Analysis.hitAny(ss, CComponents.reviveInCity, HitColors.redButton2WhiteText)
                    && Analysis.hitAny(ss, CComponents.reviveInCity, HitColors.redButton2Border)
        hasReviveRightBtn = if (hasReviveWindow) { // hasReviveRight 0.5214286
            Analysis.hit(ss, CComponents.reviveInHere, HitColors.redButton2, 12.pt, 0.38f, )  // , "reviveInHere"
                    && Analysis.hitAny(ss, CComponents.reviveInHere, HitColors.redButton2WhiteText)
                    && Analysis.hitAny(ss, CComponents.reviveInHere, HitColors.redButton2Border)
        } else false


        if (hasReviveWindow) {
            thatPage = PAGE_REVIVE
            anaLastPageReviveTs = System.currentTimeMillis()
            lastEnemyCacheTs = 0L
        }
    } else {
        hasReviveRightBtn = false
    }


    val hasBeInvitedWindow = assertInviteWindow(ss)
    if (hasBeInvitedWindow) {
        thatPage = PAGE_BE_INVITED
        anaLastPageBeInvitedTs = System.currentTimeMillis()
    }
    acceptInvite = if (hasBeInvitedWindow) assertOcrContent(ss, CComponents.inviteContent) else false
    if (maybePageMain && thatPage == PAGE_INITIAL) thatPage = PAGE_MAIN
    
    if (thatPage == PAGE_MAIN) GComponents.get().abPanel.refreshByFrame(ss)
    // inWorldPlace = if (thatPage == PAGE_MAIN && Properties.scene == C.S_RD) assertInWorldPlace(ss) else false
    rdInScene = if (thatPage == PAGE_MAIN && Properties.scene == C.S_RD) assertInScene(ss) else false
    rdFastAgain = if (thatPage == PAGE_MAIN && Properties.scene == C.S_RD) assertFastAgain(ss) else false

    if (thatPage != PAGE_MAIN) anaLastPageNotMainTs = System.currentTimeMillis()
    
    hasInteract = if (thatPage == PAGE_MAIN) assertInteract(ss) else false
    if (hasInteract) anaLastHasInteractTs = System.currentTimeMillis()

    hasBackpack100 = if (thatPage == PAGE_MAIN) assertBackpack100(ss) else false
    if (hasBackpack100) anaLastHasBackpack100Ts = System.currentTimeMillis()

    enemy = if (thatPage == PAGE_MAIN) calcEnemyName(ss) else null
    if (enemy?.isNotEmpty() == true) anaLastHasEnemyNameTs = System.currentTimeMillis()
    if (enemy == "P") anaLastHasEnemyBossNameTs = System.currentTimeMillis()
    if (enemy == "R" || enemy == "B" || enemy == "Y" || enemy == "O") anaLastHasEliteEnemyNameTs = System.currentTimeMillis()

    hasTransmitting = if (thatPage == PAGE_MAIN) assertTransmitting(ss) else false
    if (hasTransmitting) anaLastTransmittingTs = System.currentTimeMillis()

    hasApplyDot = if (thatPage == PAGE_MAIN) assertApplyDot(ss) else false
    if (hasApplyDot) anaLastApplyDotTs = System.currentTimeMillis()

    if (thatPage == PAGE_INITIAL && assertPageSkip(ss)) {
        thatPage = PAGE_SKIP
        anaLastPageSkipTs = System.currentTimeMillis()
    }
    if (thatPage == PAGE_INITIAL && assertPageBaseMenu(ss)) {
        thatPage = PAGE_BASE_MENU
        anaLastPageBaseMenuTs = System.currentTimeMillis()
    }
    if (thatPage == PAGE_INITIAL && assertPageBlacksmith(ss)) {
        thatPage = PAGE_TALK_BLACKSMITH
        anaLastPageTalkBlacksmithTs = System.currentTimeMillis()
    }
    if (thatPage == PAGE_INITIAL && assertPageApply(ss)) {
        thatPage = PAGE_APPLY
        anaLastPageApplyTs = System.currentTimeMillis()
    }
    acceptApply = if (thatPage == PAGE_APPLY) assertOcrContent(ss, GComponents.get().applyFirstName) else false

    blockSceneEnter = if (thatPage == PAGE_INITIAL && System.currentTimeMillis() - anaLastHasEnemyNameTs > 2000) calcPageSceneEnter(ss) else null
    if (blockSceneEnter != null) {
        thatPage = PAGE_SCENE_ENTER
        anaLastPageSceneEnterTs = System.currentTimeMillis()
        npcAssistBlock = if (Properties.useNpcAssist) calcNpcAssist(ss) else null
    } else npcAssistBlock = null

    if (thatPage == PAGE_INITIAL && assertPageSceneCompleted(ss)) {
        if (assertPageSceneGroupInvitePlayer(ss)) { // 邀请加入团队总览页. 确实是棕色背景很多.在这里新增二级判断
            thatPage = PAGE_SCENE_GROUP_INVITE_PLAYER
            anaLastPageSceneGroupInviteAllTs = System.currentTimeMillis()
        } else if (assertPageSceneRunAgain(ss)) {
            thatPage = PAGE_SCENE_COMPLETED
            anaLastPageSceneCompletedTs = System.currentTimeMillis()
        } else if (assertPageApplyEmpty(ss) && assertPagePopApplyClose(ss)) {
            // 确实是再开副本 thatPage = PAGE_POP_APPLY
            thatPage = PAGE_APPLY_EMPTY
            anaLastPageApplyEmptyTs = System.currentTimeMillis()
        } else {
            // 误判邀请界面.空目标的情况. 误判副本开启
        }
    }

    check()
    
    blockCloseable = if ((thatPage == PAGE_INITIAL || thatPage == PAGE_MAIN)) calcCloseButton(ss) else null
    if (blockCloseable != null) {
        thatPage = PAGE_CLOSEABLE
        anaLastPageCloseableTs = System.currentTimeMillis()
    }
    
    if (thatPage == PAGE_INITIAL && assertPagePopText(ss)) {
        thatPage = PAGE_POP_TEXT
        anaLastPagePopTextTs = System.currentTimeMillis()
    }
    if (thatPage == PAGE_INITIAL && assertPageBossDirectOrBuff(ss)) {
        thatPage = PAGE_BOSS_DIRECT_OR_BUFF
        anaLastPageBossDirectOrBuffTs = System.currentTimeMillis()
    }
    if (thatPage == PAGE_INITIAL && assertPageReviveByOther(ss)) {
        thatPage = PAGE_REVIVE_BY_OTHER
        anaLastPageReviveByOtherTs = System.currentTimeMillis()
    }
    if (thatPage == PAGE_INITIAL && assertPageTalkNpc(ss)) {
        thatPage = PAGE_TALK_NPC
        anaLastPageTalkNpcTs = System.currentTimeMillis()
    }

    if (Properties.scene == C.S_IMM || Properties.scene == C.S_TZ) {
        blockImmEnter = if (thatPage == PAGE_CLOSEABLE) calcPageImmEnter(ss) else null
        if (blockImmEnter != null) {
            thatPage = PAGE_SCENE_ENTER_IMM
        }
        blockTzEnter = if (thatPage == PAGE_CLOSEABLE) calcPageTzEnter(ss) else null
        if (blockTzEnter != null) {
            thatPage = PAGE_SCENE_ENTER_TZ
        }
        if (thatPage == PAGE_INITIAL && assertPageImmKey(ss)) {
            thatPage = PAGE_IMM_KEY
        }
        if ((thatPage == PAGE_INITIAL || thatPage == PAGE_SCENE_COMPLETED) && assertPageDailySign(ss)) {
            thatPage = PAGE_IMM_DAILY_SIGN // [5][铁匠][再次挑战]
        }
    }


    blockBlackGroup = if (thatPage == PAGE_INITIAL) calcBlackGroup(ss) else null
    if (blockBlackGroup != null) {
        thatPage = PAGE_BE_INVITED_BLACK_GROUP
        anaLastPageBeInvitedBlackGroupTs = System.currentTimeMillis()
    }
    if (thatPage == PAGE_INITIAL && assertPageAtv(ss)) {
        thatPage = PAGE_ATV
        anaLastPageAtvTs = System.currentTimeMillis()
    }
    if (thatPage == PAGE_INITIAL
        && System.currentTimeMillis() - xLastUnknownTs > 16000
        && assertPageEditFriendClose(ss)
        && (Properties.scene != C.S_IMM)) {
        thatPage = PAGE_EDIT_FRIEND_NAME
        anaLastPageEditFriendCloseTs = System.currentTimeMillis()
    }
    if (blockCloseable != null && thatPage != PAGE_TALK_BLACKSMITH && assertPageSelectRole(ss)) {
        thatPage = PAGE_SELECT_ROLE
    }
    if (thatPage == PAGE_INITIAL && assertPageRootEnter(ss)) {
        thatPage = PAGE_ROOT_ENTER
    }
    if (thatPage == PAGE_INITIAL && assertPageRootEnterTipWindow(ss)) {
        thatPage = PAGE_ROOT_ENTER_TIP
    }
    if (Properties.scene == C.S_GBWJ) {
        if (thatPage == PAGE_INITIAL && assertGBFWPageFinished(ss)) {
            thatPage = PAGE_GBWJ_COMPLETED
            anaLastPageGBWJCompletedTs = System.currentTimeMillis()
        }
        if (thatPage == PAGE_INITIAL && assertGBFWPageRevive(ss)) {
            thatPage = PAGE_GBWJ_REVIVE
        }
    }
    if (Properties.scene == C.S_LX || Properties.scene == C.S_DYLX) {
        sceneRedStateTitle = if (thatPage == PAGE_MAIN) assertRedStateTitle(ss) else false

        val a = if (sceneRedStateTitle) assertLxInProgress(ss) else false
        lxInProgress = a || System.currentTimeMillis() - lxInProgressTs < 2000
        if (a) lxInProgressTs = System.currentTimeMillis()

        if (thatPage == PAGE_INITIAL && assertGBFWPageRevive(ss)) {
            thatPage = PAGE_LX_REVIVE
        }
        if (thatPage == PAGE_INITIAL && assertLXPageUseConfirm(ss)) {
            thatPage = PAGE_LX_USE_CONFIRM
            val out = assertLXConfirmLack(ss)
            lxUseConfirmTs = System.currentTimeMillis()
            if (out) anaLastLxLackOfMaterialsTs = System.currentTimeMillis()
        }
    } else if (Properties.scene == C.S_DY2 || Properties.scene == C.S_DY) {
        if (thatPage == PAGE_INITIAL) {
            val out = calcMonsterEssence(ss)
            blockMapMonsterEssence = out
            if (out != null) anaLastMapMonsterEssenceTs = System.currentTimeMillis()
            blockMapMonsterEssenceBtn = if (out != null) {
                Block("mapMonsterEssenceBtn", out.x + 160.pt.shake(20.pt), out.y - 15.pt, 30.pt, 30.pt)
            } else null
        }
    }

    if (thatPage == PAGE_MAIN // 一般寻路不战斗中需要触发这个判定.
        && System.currentTimeMillis() - xLastAtkTs > 15000) {
        bitmapLastScreenshot?.also {
            if (Analysis.compareWithLast(ss, it, GComponents.get().smallMapCenter, 22.pt, 0.7f, 5.pt)) {
                printLog { "analyst Action Stopped $counterActionStopped" }
                anaLastActionStoppedTs = System.currentTimeMillis()
            }
            if (System.currentTimeMillis() - anaLastActionStoppedTs < 2000) {
                counterActionStopped++
            } else counterActionStopped = 0
        }
        bitmapLastScreenshot = ss.copy(Bitmap.Config.ARGB_8888, false)
    } else counterActionStopped = 0



    if (bsHasEnemy) GComponents.syncRefreshSkillIfNeed(ss)

    if (thatPage == PAGE_MAIN) analysisMap(ss)
    if (thatPage != PAGE_MAIN) continueCounter = 10000 // 重要逻辑.必须有 <切换到位置页面再回来.比如地图转场.摇杆要清掉一次.比如疯王>

    if (thatPage == PAGE_INITIAL) {
        val time = accLastInvokedTs / 1000
        if (time >= 2L) xLastUnknownTs = System.currentTimeMillis() // 做个标记
        if (bsSceneSettle && time >= 7L) anaClearSceneSettle() // 结算清除.比如退出了副本
    }

    page = thatPage
}



private fun assertPageMain(ss: Bitmap): Boolean {
    if (GComponents.skillPrepared
        && System.currentTimeMillis() - GComponents.get().abPanel.atk.lastClickedTs < 40000
        && Analysis.compareSkillBitmap(ss, GComponents.get().abPanel.atk.view, GComponents.get().abPanel.atk.bitmap, 12.pt, 0.65f)
    ) { // , "testSkillAtk" total [8.pt]213/225  0.94
        anaCounterNoSkill = 0
        return true // 没有就是0
    }
    if (GComponents.skillPrepared
        && System.currentTimeMillis() - GComponents.get().abPanel.skillB.lastClickedTs < 40000
        && Analysis.compareSkillBitmap(ss, GComponents.get().abPanel.skillB.view, GComponents.get().abPanel.skillB.bitmap, 7.pt, 0.65f)
    ) { // , "testSkillB"
        anaCounterNoSkill = 0
        return true
    }
    if (GComponents.skillPrepared
        && System.currentTimeMillis() - GComponents.get().abPanel.skillC.lastClickedTs < 40000
        && Analysis.compareSkillBitmap(ss, GComponents.get().abPanel.skillC.view, GComponents.get().abPanel.skillC.bitmap, 7.pt, 0.65f)
    ) { // , "testSkillC"
        anaCounterNoSkill = 0
        return true
    }

    if (System.currentTimeMillis() - xLastAtkTs < 3000) anaCounterNoSkill = 0
    if (bsPage == PAGE_MAIN) anaCounterNoSkill++ // 被键盘卡住

    val hasHp = hasHp(ss)
    if (!hasHp) return false
    val hasMainWidgets = hasMainWidgets(ss) // 开E技能攻击按钮变了
    if (hasMainWidgets) return true
    val hasLv = Analysis.hitAny(ss, GComponents.get().lv, HitColors.hitLv) // hasLv
    if (hasLv) return true
    if (blockEliteEnemyBoss != null) return true // 看的见地图boss
    return false

}
private fun hasHp(ss: Bitmap?): Boolean {
    if (ss == null) return false
    val block = GComponents.get().selfHp // height 24.pt
    var x = block.right - 1
    while(x >= block.left) {
        val c = App.take().takePixel(ss, x, block.y)
        val p = (x - block.left) * 1f / block.width
        if (HitColors.hitSelfHp.containsSimilarColor(c)) { // 1%血量不到可以查黑色
            val check1 = Block("hpContent", block.left, block.top, x - block.left, block.height)
            val check2 = Block("hpContentBlack", x, block.top, block.right - x, block.height)
            val c1 = if (p < 0.1) true else Analysis.hit(ss, check1, HitColors.hitSelfHp, 4, 0.5f) // 0.9一般 // , "hpContent"
            val c2 = if (p > 0.9) true else Analysis.hit(ss, check2, HitColors.hitSelfHpBlack, 4, 0.6f) // 0.9一般 // , "hpContentBlack"
            if (c1 && c2) return true
        }
        x -= 1
    }
    return false
}
private fun hasMainWidgets(ss: Bitmap?): Boolean {
    if (ss == null) return false
    return Analysis.hit(ss, GComponents.get().menuPrimary, HitColors.hitMenu, 6.pt, 0.62f) &&
            Analysis.hit(ss, GComponents.get().menuPackage, HitColors.hitMenu, 6.pt, 0.62f)// 开E技能攻击按钮变了
}
private fun calcHpPercent(ss: Bitmap?): Float {
    if (ss == null) return 0f
    val block = GComponents.get().selfHp // height 24.pt
    var x = block.right - 1
    while(x >= block.left) {
        val c = App.take().takePixel(ss, x, block.y)
        val p = (x - block.left) * 1f / block.width
        if (HitColors.hitSelfHp.containsSimilarColor(c)) { // 1%血量不到可以查黑色
            val check1 = Block("hpContent", block.left, block.top, x - block.left, block.height)
            val check2 = Block("hpContentBlack", x, block.top, block.right - x, block.height)
            val c1 = if (p < 0.1) true else Analysis.hit(ss, check1, HitColors.hitSelfHp, 4, 0.5f) // 0.9一般 // , "hpContent"
            val c2 = if (p > 0.9) true else Analysis.hit(ss, check2, HitColors.hitSelfHpBlack, 4, 0.5f) // 0.9一般 // , "hpContentBlack"
            if (c1 && c2) {
                return p
            }
        }
        x -= 1
    }
    return 0f // 0 变成算作子页面
}

private fun assertInviteWindow(ss: Bitmap): Boolean {
    return Analysis.hit(ss, CComponents.checkInviteCancel, HitColors.hitRedButtonLeftPart1, 3.pt, 0.5f)
            && Analysis.hit(ss, CComponents.checkInviteConfirm, HitColors.hitRedButtonLeftPart1, 3.pt, 0.5f)
            && Analysis.hit(ss, CComponents.checkInviteCancelTop, HitColors.hitInviteCancelTop, 3.pt, 0.4f)
}
private fun assertOcrContent(ss: Bitmap?, rectBlock: Block): Boolean {
    return AnalysisInvite.conversion(ss, rectBlock)
}
private val hitAutoDYCheckWhite by lazy { setOf(0xffA9A987.toInt().rgb,) }
private val hitAutoDYCheckAll by lazy {
    setOf(
        0xff040504.toInt().rgb(6),
        0xffA9A987.toInt().rgb, // 白色
    )
}
private fun assertInWorldPlace(ss: Bitmap?): Boolean {
    if (ss == null) return false
    return Analysis.hitAny(ss, GComponents.get().autoDYBtnCheck, hitAutoDYCheckWhite, 3.pt)
            //  hit [autoDYBtnCheck] 0.5123967 hitCount 62 total 121
            && Analysis.hit(ss, GComponents.get().autoDYBtnCheck, hitAutoDYCheckAll, 3.pt, 0.4f, "autoDYBtnCheck")
}
private val hitInScene by lazy {
    setOf(
        0xff574A38.toInt().rgb(16),
        0xff453E2E.toInt().rgb(6), //法墓P6
    )
}
private fun assertInScene(ss: Bitmap?): Boolean {
    if (ss == null) return false
    // hit [inScene] 0.1875 hitCount 21 total 112
    // hit [inScene] 0.09090909 hitCount 18 total 198
    return Analysis.hit(ss, GComponents.get().inScene, hitInScene, 4.pt, 0.03f, ) //inScene
}
private val hitFastAgainBtn by lazy {
    setOf(
        0xffFFFFCD.toInt().rgb,
        0xff687429.toInt().rgb(32),
    )
}
private val hitFastAgainCancelBtn by lazy {
    setOf(
        0xffF8E2E1.toInt().rgb,
        0xff8C3D23.toInt().rgb,
        0xff602D1C.toInt().rgb,
    )
}
private fun assertFastAgain(ss: Bitmap?): Boolean {
    if (ss == null) return false
    // hit [fastAgainBtn] 0.6666667 hitCount 54 total 81
    // hit [fastAgainCancelBtn] 0.5555556 hitCount 45 total 81
    return Analysis.hit(ss, GComponents.get().fastAgainBtn, hitFastAgainBtn, 3.pt, 0.33f, ) // fastAgainBtn
            && Analysis.hit(ss, GComponents.get().fastAgainCancelBtn, hitFastAgainCancelBtn, 3.pt, 0.33f, ) // fastAgainCancelBtn
}


private val interact by lazy { GComponents.get().abPanel.interact.view }
private val hitInteractAll by lazy {
    setOf(
        0xffE8D010.toInt().rgb(25),
        0xffCA9B0D.toInt().rgb(25, 25, 15),
//          失败了没出就不要了 0xffFCF73C.toInt().rgb(5, 5, 5), // 回音踢腿
//          失败了没出就不要了 0xffCC8017.toInt().rgb(8, 15, 8), // 回音踢腿
    )
}
private val checkTopBox by lazy {
    Block("宝箱特征", interact.right - 30.pt, interact.top - 10.pt, 20.pt, 20.pt)
}
private val hitAllBox by lazy {
    setOf(
        0xfffce22d.toInt().rgb(25),
        0xffebd342.toInt().rgb(25),
        0xffd7b718.toInt().rgb(25),
        0xffa76013.toInt().rgb(25),
    )
}
private val hitTopBox by lazy {
    setOf(0xffb36706.toInt().rgb(25, 25, 15),)
}
private fun assertInteract(ss: Bitmap?): Boolean {
    if (ss == null) return false
    if (Analysis.hit(ss, interact, hitInteractAll, 6.pt, 0.2f)) {
        return true
    }
    // [宝箱top] 0.46280992 hitCount 56 total 121
    // [宝箱A] 0.5969388 hitCount 117 total 196
    return (Analysis.hit(ss, checkTopBox, hitTopBox, 3.pt, 0.38f)   //  [宝箱A] 0.5969388 hitCount 117 total 196
            && Analysis.hit(ss, interact, hitAllBox, 6.pt, 0.42f))
}

private val hitEnemyNameW by lazy {
    setOf(
        0xffd9d6d2.toInt().rgb(50, 20, 20), // 米白1
        0xffccc4be.toInt().rgb(50, 20, 20), // 米白2
        0xff827064.toInt().rgb(50, 20, 20), // 低血量米白
    )
}
private val hitEnemyNameB by lazy {
    setOf(
        0xff4c52a1.toInt().rgb(50, 20, 20), // 蓝怪1
        0xff666ed8.toInt().rgb(50, 20, 20), // 蓝怪2
    )
}
private val hitEnemyNameP by lazy {
    setOf(
        0xffa058d4.toInt().rgb(50, 20, 20), // 紫怪1
        0xff9557d5.toInt().rgb(50, 20, 30), // 紫怪2

//        0xFF753F9A.toInt().rgb  Count: 3
//        0xFF753F9B.toInt().rgb  Count: 4
//        0xFF77409D.toInt().rgb  Count: 3
//        0xFF7C44A4.toInt().rgb  Count: 3
//        0xFF8548B0.toInt().rgb  Count: 3
//        0xFF9551C4.toInt().rgb  Count: 3
//        0xFF9D55CE.toInt().rgb  Count: 3
//        0xFF9F56D1.toInt().rgb  Count: 3
//        0xFFA158D4.toInt().rgb  Count: 3
//        0xFFA55AD9.toInt().rgb  Count: 3
//        0xFFA65ADA.toInt().rgb  Count: 4
//        0xFFA75BDC.toInt().rgb  Count: 3
//        0xFFA85BDD.toInt().rgb  Count: 3
//        0xFFAB5DE1.toInt().rgb  Count: 3
//        0xFFAC5EE3.toInt().rgb  Count: 5
//        0xFFAE5FE5.toInt().rgb  Count: 3
//        0xFFAE5FE6.toInt().rgb  Count: 3
//        0xFFAF60E7.toInt().rgb  Count: 4
//        0xFFB160E9.toInt().rgb  Count: 3
//        0xFFB262EC.toInt().rgb  Count: 3
//        0xFFB563EF.toInt().rgb  Count: 4
//        0xFFB764F2.toInt().rgb  Count: 5
//        0xFFB865F4.toInt().rgb  Count: 5
//        0xFFB965F5.toInt().rgb  Count: 3
//        0xFFBA66F6.toInt().rgb  Count: 4
//        0xFFBB66F7.toInt().rgb  Count: 3
//        0xFFBC67F9.toInt().rgb  Count: 7
//        0xFFBD67FA.toInt().rgb  Count: 6
//        0xFFBE68FB.toInt().rgb  Count: 5
//        0xFFBF69FD.toInt().rgb  Count: 6
//        0xFFC069FE.toInt().rgb  Count: 3
    )
}
private val hitEnemyNameY by lazy {
    setOf(
        0xffdddb52.toInt().rgb(50, 20, 20), // 绿黄1
        0xff9a9839.toInt().rgb(50, 20, 20), // 绿黄2
        0xffd89d10.toInt().rgb(50, 20, 20), // 屎黄1 狂信徒牧师 屎黄名字
    )
}
private val hitEnemyNameO by lazy { // 容易误判.迷雾平原. 所以看地图
    setOf(
        0xffe47949.toInt().rgb(35, 10, 10), // 橙1
        0xffb36e4d.toInt().rgb(35, 10, 10), // 橙2
        0xff79452c.toInt().rgb(35, 15, 10), // 低血量橙2
    )
}
private val hitEnemyNameR by lazy {  // 高地狱等级
    setOf(
        0xffed8947.toInt().rgb(35, 20, 20), //
        0xffc5723b.toInt().rgb(35, 20, 20), //
        0xffc5743c.toInt().rgb(35, 20, 20), //

        0xFFFF773B.toInt().rgb(6), // 高地狱等级
        0xFFFE8633.toInt().rgb(6), // 高地狱等级
    )
}
private val hitEnemyName by lazy {
    val output = mutableSetOf<ColorDiffer>()
    output.addAll(hitEnemyNameW)
    output.addAll(hitEnemyNameP) // 先P后B
    output.addAll(hitEnemyNameB)
    output.addAll(hitEnemyNameY)
    output.addAll(hitEnemyNameO) // 先O后R
    output.addAll(hitEnemyNameR)
    output
}
private val hitEnemyNameExcludeO by lazy {
    val output = mutableSetOf<ColorDiffer>()
    output.addAll(hitEnemyNameW)
    output.addAll(hitEnemyNameP) // 先P后B
    output.addAll(hitEnemyNameB)
    output.addAll(hitEnemyNameY)
    output.addAll(hitEnemyNameR)
    output
}
private val hitEnemyNameExcludeOR by lazy {
    val output = mutableSetOf<ColorDiffer>()
    output.addAll(hitEnemyNameW)
    output.addAll(hitEnemyNameP) // 先P后B
    output.addAll(hitEnemyNameB)
    output.addAll(hitEnemyNameY)
    output
}
private val checkEnemyName by lazy {
    Block("敌人名称", phoneScreenWidth / 2 - 20.pt,  0, 40.pt, 50.pt)
}
private val checkEnemyHpContentLeft by lazy {
    Block("敌人血量(左)", phoneScreenWidth / 2 - 40.pt, 50.pt, 40.pt, 40.pt)
}
private val checkEnemyHpContentRight by lazy {
    Block("敌人血量(右)", phoneScreenWidth / 2, 50.pt, 40.pt, 40.pt)
}
private val checkEnemyHpContent by lazy {
    Block("敌人血量", phoneScreenWidth / 2 - 40.pt, 50.pt, 80.pt, 40.pt)
}
private val checkEnemyHpProgress by lazy {
//    Prefab.requirePrefab(Prefab.ITEM_ENEMY_HP_PROGRESS)?.parseToBlock()
//        ?:
    Block(C.V_ENEMY_HP, phoneScreenWidth / 2 - 210.pt, 63.pt, 420.pt, 5.pt)
}
private val hitEnemyHpBorder by lazy {
    setOf(
        0xff2e2925.toInt().rgb(50, 20, 20),
        0xff322d28.toInt().rgb(50, 20, 20),
        0xff42362f.toInt().rgb(50, 20, 20),
        0xff393232.toInt().rgb(50, 20, 20),
        0xff47473c.toInt().rgb(50, 20, 20),
    )
}
private val hitEnemyHpValue by lazy {
    setOf(
        0xffc75c30.toInt().rgb(50, 20, 20),
        0xfff17038.toInt().rgb(50, 20, 20), // 橙色血
        0xffd8d4d0.toInt().rgb(50, 20, 20),
        0xffd3c3ca.toInt().rgb(50, 20, 20), // 白
        0xffd36f43.toInt().rgb(50, 20, 20), // 高战斗等级
    )
}
private val hitEnemyHp by lazy {
    setOf(
        0xffa32c12.toInt().rgb(50, 20, 20),
        0xff7c210a.toInt().rgb(50, 20, 20),
        0xffbd3417.toInt().rgb(50, 20, 20), // 血
//          0xff454545.toInt().rgb(50, 20, 20), 0xff363636.toInt().rgb(50, 20, 20), // 水泥铁色 [托纳克]. 不管.增加误差
        0xffDC8917.toInt().rgb(50, 20, 20), // 回音新增1
        0xfffc8a3b.toInt().rgb(50, 20, 20), // 回音新增2
    )
}
private fun calcEnemyName(ss: Bitmap?) : String? {
    if (ss == null) return null
    if (Analysis.hitAny(ss, checkEnemyHpContentLeft, hitEnemyHpBorder, 2.pt)) {
        if (Analysis.hitAny(ss, checkEnemyHpContentRight, hitEnemyHpBorder, 2.pt)) {
            if (Analysis.hitAny(ss, checkEnemyHpContent, hitEnemyHpValue, 2.pt)) {
                if (Analysis.hitAny(ss, checkEnemyHpProgress, hitEnemyHp, 2.pt)) {
                    val hitEnemyNameColor = when (excludeEnemyName) {
                        "RO" -> hitEnemyNameExcludeOR
                        "O"-> hitEnemyNameExcludeO
                        else -> hitEnemyName
                    }
                    val namePoint = Analysis.hitAnyPoint(ss, checkEnemyName, hitEnemyNameColor, 2.pt)
                    if (namePoint != null) { // 以上基本特征判断. 这个是为了分析出敌人种类
                        val hpHitColors = hitEnemyHp
                        var x = phoneScreenWidth / 2 - 250.pt
                        while (x < phoneScreenWidth / 2) {
                            var y = 64.pt
                            while (y < 72.pt) {
                                if (hpHitColors.containsSimilarColor(App.take().takePixel(ss, x, y))) {
                                    val check = Block("血条检测", x, 64.pt, 16.pt, 8.pt)
                                    if (Analysis.hit(ss, check, hpHitColors, 2.pt, 0.6f)) { // 1.0
                                        val out = parseEnemyName(App.take().takePixel(ss, namePoint.first, namePoint.second))
                                        lastEnemyCache = out
                                        lastEnemyCacheTs = System.currentTimeMillis()
                                        return out // "敌人名称鉴别[${lastEnemyCache}]real"
                                    }
                                }
                                y += 4.pt
                            }
                            x += 3.pt
                        }
                    }
                }
            }
        }
    }
    // 下面返回保证一直在检测.名字连续存在
    if (lastEnemyCache?.isNotEmpty() == true && System.currentTimeMillis() - lastEnemyCacheTs < 400) {
        return lastEnemyCache // "敌人名称鉴别[${lastEnemyCache}]cacheIn500-a"
    }
    return null
}
private fun parseEnemyName(nameColor: Int): String {
    return when {
        hitEnemyNameW.containsSimilarColor(nameColor) -> "W"
        hitEnemyNameB.containsSimilarColor(nameColor) -> "B"
        hitEnemyNameP.containsSimilarColor(nameColor) -> "P" // 回音最后的boss可能被B遮挡
        hitEnemyNameY.containsSimilarColor(nameColor) -> "Y"
        hitEnemyNameO.containsSimilarColor(nameColor) -> "O"
        hitEnemyNameR.containsSimilarColor(nameColor) -> "R"
        else -> "未知"
    }
}

private fun assertTransmitting(ss: Bitmap): Boolean {
    return Analysis.hit(ss, CComponents.checkTransmittingBorderLeft, HitColors.hitTransmittingBorderLeft, 1, 0.01f) // , "checkTransmittingBorderLeft"
            && Analysis.hit(ss, CComponents.checkTransmittingBorderRight, HitColors.hitTransmittingBorderRight, 1, 0.01f) // , "checkTransmittingBorderRight"
            && Analysis.hit(ss, CComponents.checkTransmitting, HitColors.hitTransmitting, 1, 0.05f) // , "checkTransmitting"
}

private fun assertApplyDot(ss: Bitmap): Boolean {
    //  [applyRedDot] 0.10204082 hitCount 5 total 49
    return Analysis.hit(ss, GComponents.get().applyRedDot, HitColors.hitApplyDot, 5.pt, 0.02f)
}
private fun assertPageSkip(ss: Bitmap): Boolean {
    return Analysis.hit(ss, GComponents.get().skippable, HitColors.hitSkippable, 1, 0.18f)
            && Analysis.hitAny(ss, GComponents.get().skippable, HitColors.hitSkippableBlack)
}
private val hitBaseMenuRed by lazy {
    setOf(
        0xff5e2d21.toInt().rgb, 0xff53251a.toInt().rgb,
        0xff4a241c.toInt().rgb, 0xff381c13.toInt().rgb,
    )
}
private val hitBaseMenuWhite by lazy {
    setOf(
        0xffc3ab8d.toInt().rgb, 0xffc3ac92.toInt().rgb,
        0xffbaa374.toInt().rgb, 0xffbb9977.toInt().rgb,
    )
}
private val hitBaseMenuContent by lazy {
    val output = mutableSetOf<ColorDiffer>()
    output.addAll(hitBaseMenuRed)
    output.addAll(hitBaseMenuWhite)
    output
}
private fun assertPageBaseMenu(ss: Bitmap): Boolean {
    if (!Analysis.hitAny(ss, GComponents.get().baseMenuLine1, hitBaseMenuWhite, 2.pt)) return false
    if (!Analysis.hitAny(ss, GComponents.get().baseMenuLine2, hitBaseMenuWhite, 2.pt)) return false
    if (!Analysis.hitAny(ss, GComponents.get().baseMenuLine3, hitBaseMenuWhite, 2.pt)) return false
    return Analysis.hit(ss, GComponents.get().baseMenuLine1, hitBaseMenuContent, 6.pt,0.6f) // ,"basicMenu1" // 0.9583333
            && Analysis.hit(ss, GComponents.get().baseMenuLine2,
        hitBaseMenuContent, 6.pt, 0.6f)  // , "basicMenu2" // 0.8263889
            && Analysis.hit(ss, GComponents.get().baseMenuLine3,
        hitBaseMenuContent, 6.pt, 0.6f)  // , "basicMenu3" // 0.8680556
}
private fun assertPageBlacksmith(ss: Bitmap): Boolean {
    return Analysis.hit(ss, CComponents.sceneNpcChatPart, HitColors.hitSceneSMTalkPart, 18.pt, 0.5f,) // ""npc聊天内容背景"" 1.0  1.0 hitCount 132 total 132
            && Analysis.hit(ss, GComponents.get().equipmentDecomposition, HitColors.hitRedButtonEquipmentDecomposition, 20.pt, 0.6f) //  hit [红色按钮] 0.8804348 hitCount 81 total 92
}
private fun assertPageApply(ss: Bitmap): Boolean {
    return Analysis.hit(ss, CComponents.applyPageTitle, HitColors.hitApplyTitle, 20.pt, 0.6f)
            && Analysis.hit(ss, CComponents.applyButtonLeft, HitColors.hitApplyButton, 4.pt, 0.4f)
            && Analysis.hit(ss, CComponents.applyButtonRight, HitColors.hitApplyButton, 4.pt, 0.4f)
}
private fun assertPageApplyEmpty(ss: Bitmap): Boolean {
    return Analysis.hit(ss, CComponents.applyPageTitle, HitColors.hitApplyTitle, 20.pt, 0.6f)
}
private fun assertPageSceneCompleted(ss: Bitmap): Boolean {
    return Analysis.hit(ss, GComponents.get().sceneCompletedTitleBg, HitColors.hitSceneCompletedTitle, 18.pt, 0.7f,) // "顶部灰色" 1.0 hitCount 66 total 66
            && Analysis.hit(ss, GComponents.get().sceneCompletedBottomBg, HitColors.hitSceneCompletedBottom, 18.pt, 0.7f) // hit [底部灰色] 1.0 hitCount 66 total 66
}
private fun assertPageSceneGroupInvitePlayer(ss: Bitmap): Boolean {
    val block = CComponents.sceneInviteGroupAllClose
    if (Analysis.hit(ss, block, hitCloseContent, 6.pt, 0.5f) &&
        Analysis.hitAny(ss, block, hitCloseWhite, 3.pt)) { //, "closeBtnContent" // minPercent = 0.75206614 左右
        // 还有2个人物邀请+按钮判断 [测试过 能区分]. 1个人
        //, "closeBtnContent" // minPercent = 0.75206614 左右
        // hit [emptyPlayer] 0.1734694 hitCount 34 total 196 如果再次挑战就是0
        if (Analysis.hit(ss, CComponents.sceneInviteGroupPlayerBtn1, hitCloseContent, 4.pt, 0.1f)
            || Analysis.hit(ss, CComponents.sceneInviteGroupPlayerBtn2, hitCloseContent, 4.pt, 0.1f)
            || Analysis.hit(ss, CComponents.sceneInviteGroupEmpty, hitInvitePlayerEmpty, 4.pt, 0.1f, )) { // emptyPlayer
            return true
        }
    }
    return false
}

private val hitSceneEnterRed by lazy {
    setOf(
        0xff6d2b15.toInt().rgb(10), 0xff7a3015.toInt().rgb(10),
        0xff9a4216.toInt().rgb(10), 0xff863314.toInt().rgb(10),
        0xff652510.toInt().rgb(10),
    )
}
private val hitSceneEnterWhite by lazy {
    setOf(
        0xfffefdd1.toInt().rgb, 0xfff0e5bb.toInt().rgb,
    )
}
val hitSceneEnterButton by lazy {
    val output = mutableSetOf<ColorDiffer>()
    output.addAll(hitSceneEnterRed)
    output.addAll(hitSceneEnterWhite)
    output
}
private val hitCloseContent by lazy {
    val output = mutableSetOf<ColorDiffer>()
    output.addAll(hitCloseRed)
    output.addAll(hitCloseWhite)
    output
}
private val hitCloseContentDark by lazy {
    val output = mutableSetOf<ColorDiffer>()
    output.addAll(hitCloseRedDark)
    output.addAll(hitCloseWhiteDark)
    output
}
private val hitInvitePlayerEmpty by lazy {
    setOf(
        0xffF7ECB7.toInt().rgb(4),
    )
}
private fun saveSceneEnterBlock(block: Block) {
    writeToDisk("sceneEnterBlockLeft", block.left.toString())
    writeToDisk("sceneEnterBlockTop", block.top.toString())
    writeToDisk("sceneEnterBlockWidth", block.width.toString())
    writeToDisk("sceneEnterBlockHeight", block.height.toString())
}
private var cSceneEnterBlock: Block? = null
val cacheSceneEnterBlock: Block? get() {
    val c= cSceneEnterBlock
    if (c != null) return c
    val left = readByDisk("sceneEnterBlockLeft")?.toIntOrNull() ?: return null
    val top = readByDisk("sceneEnterBlockTop")?.toIntOrNull() ?: return null
    val width = readByDisk("sceneEnterBlockWidth")?.toIntOrNull() ?: return null
    val height = readByDisk("sceneEnterBlockHeight")?.toIntOrNull() ?: return null
    val block = Block(btnSceneStartEnter, left, top, width, height)
    cSceneEnterBlock = block
    return block
}
private fun calcPageSceneEnter(ss: Bitmap): Block? {
    // 先扫描叉的位置
    // 右边往左最多 310pt
    val sceneCloseY = 160.pt
    val sceneCloseCheckEndY = 171.pt
    var x = phoneScreenWidth - 1
    val endX = phoneScreenWidth - 330.pt
    var block: Block? = null // 副本组队关闭
    while (x > endX) {
        var y = sceneCloseY
        while (y < sceneCloseCheckEndY) {
            if (hitCloseContent.containsSimilarColor(App.take().takePixel(ss, x, y))) {
                val check = Block("副本组队文字", x, y, 40.pt, 40.pt)
                // hit [hitCloseContent] 0.765625 hitCount 49 total 64
                if (Analysis.hit(ss, check, hitCloseContent, 6.pt, 0.5f) &&
                    Analysis.hitAny(ss, check, hitCloseWhite, 3.pt)) { //, "closeBtnContent" // minPercent = 0.75206614 左右
                    block = Block("副本组队文字2", x, y, 20.pt, 40.pt) // 怕点不到
                    break
                }
            }
            y += 9.pt
        }
        x -= 10.pt
    }

    val sceneCloseButton = block
    if (sceneCloseButton != null) {
        // 有关闭按钮的副本组队入口
        val buttonEnter = Block(btnSceneStartEnter,  sceneCloseButton.left - 260.pt, 770.pt, 180.pt, 40.pt)
        val buttonSearch = Block("副本组队寻找", sceneCloseButton.left - 600.pt, 770.pt, 180.pt, 40.pt)
        // hit [buttonEnter] 0.7692308 hitCount 30 total 39
        // hit [buttonSearch] 0.7948718 hitCount 31 total 39
        // hit [buttonEnter] 0.84615386 hitCount 33 total 39
        // hit [buttonEnter] 0.71794873 hitCount 28 total 39
        if (Analysis.hit(ss, buttonEnter, hitSceneEnterButton, 14.pt, 0.54f,) // buttonEnter
            && Analysis.hit(ss, buttonSearch, hitSceneEnterButton, 14.pt, 0.54f,)) { // buttonSearch
            printLog { "XRoleBase saveSceneEnterBlock 保存副本入口缓存1" }
            saveSceneEnterBlock(buttonEnter)
            return buttonEnter
        }
    }

    // 没有关闭按钮的副本入口
    val sceneButtonY = 770.pt
    var m = phoneScreenWidth - 1
    val endM = phoneScreenWidth - 600.pt // 330.pt - 180.pt
    while (m > endM) {
        if (hitSceneEnterButton.containsSimilarColor(App.take().takePixel(ss, m, sceneButtonY))) {
            val buttonEnterB = Block("副本组队进入B",  m, 770.pt, 180.pt, 40.pt)
            val buttonSearchB = Block("副本组队寻找B", m - 340.pt, 770.pt, 180.pt, 40.pt)
            // hit [buttonEnterB] 0.9230769 hitCount 36 total 39
            // hit [buttonSearchB] 0.5641026 hitCount 22 total 39
            // buttonEnterB] 0.8974359 hitCount 35 total 39
            // [buttonSearchB] 0.5897436 hitCount 23 total 39
            if (Analysis.hit(ss, buttonEnterB, hitSceneEnterButton, 14.pt, 0.64f, "") // buttonEnterB
                && Analysis.hit(ss, buttonSearchB, hitSceneEnterButton, 14.pt, 0.54f, "")) { // buttonSearchB
                // 调整下点击位置
                val buttonEnter2 =  Block(btnSceneStartEnter, m - 5.pt, 770.pt, 10.pt, 40.pt)
              //  printLog { "XRoleBase saveSceneEnterBlock 保存副本入口缓存2  $m $phoneScreenWidth ${m * 1f / phoneScreenWidth}" }
                saveSceneEnterBlock(buttonEnter2)
                return buttonEnter2
            }
        }
        m -= 10.pt
    }
    return null
}
private val hitCloseRed by lazy {
    setOf(
        0xff652510.toInt().rgb,
        0xff6d2b15.toInt().rgb,
        0xff7a3015.toInt().rgb,
        0xff863314.toInt().rgb,
        0xff9a4216.toInt().rgb,
    )
}

private val hitCloseWhite by lazy {
    setOf(
        0xffffffe7.toInt().rgb, 0xffffffdd.toInt().rgb,
        0xffeeeecf.toInt().rgb,
    )
}

private val hitCloseRedDark by lazy {
    setOf(
        // 深色模式 //残血
        0xff502A19.toInt().rgb,
    )
}
private val hitCloseWhiteDark by lazy {
    setOf(
        // 深色残血
        0xffBEC0AD.toInt().rgb,
    )
}

private val closeButtonCheckPart: Rect get() = GComponents.get().closeCheckPart.rect
// 2ms - 4ms
private fun calcCloseButton(ss: Bitmap): Block? {
    var x = closeButtonCheckPart.right - 1
    while (x > closeButtonCheckPart.left) {
        var y = closeButtonCheckPart.top
        while (y < closeButtonCheckPart.bottom) {
            if (hitCloseRed.containsSimilarColor(App.take().takePixel(ss, x, y))) {
                val block = Block("close", x - 40.pt, y, 40.pt, 40.pt)
                if (Analysis.hit(ss, block, hitCloseContent, 6.pt, 0.5f) &&
                    Analysis.hitAny(ss, block, hitCloseWhite, 3.pt)) { //, "closeBtnContent" // minPercent = 0.75206614 左右
                    return block
                }
            }
            y += 10.pt
        }
        x -= 10.pt
    }
    return null
}

private fun assertPagePopText(ss: Bitmap): Boolean {
    return Analysis.hit(ss, CComponents.waitOtherCancel, HitColors.hitWaitOtherRedButton, 3, 0.6f) &&
            Analysis.hit(ss, CComponents.waitOtherConfirm, HitColors.hitWaitOtherRedButton, 3, 0.6f) &&
            Analysis.hitAny(ss, CComponents.waitOtherCancel, HitColors.hitWaitOtherText) &&
            Analysis.hitAny(ss, CComponents.waitOtherConfirm, HitColors.hitWaitOtherText)
}
private fun assertPageBossDirectOrBuff(ss: Bitmap): Boolean {
    return Analysis.hit(ss, CComponents.checkRandomLeft, HitColors.redButton2, 11.pt, 0.24f) // 0.6 "reviveInCity"
            && Analysis.hit(ss, CComponents.checkRandomRight, HitColors.redButton2, 11.pt, 0.24f)
            && Analysis.hitAny(ss, CComponents.checkRandomCenterText, HitColors.redButton2WhiteText)
}
private fun assertPageReviveByOther(ss: Bitmap): Boolean {
    return Analysis.hit(ss, CComponents.checkReviveByOtherCancel, HitColors.hitRedButtonLeftPart1, 4, 0.7f) &&
            Analysis.hit(ss, CComponents.checkReviveByOtherAccept, HitColors.hitRedButtonLeftPart1, 4, 0.7f)
}
// 暗影会邀请
private val hitBlackInviteRed by lazy {
    setOf(
        0xff6d2b15.toInt().rgb, 0xff7a3015.toInt().rgb,
        0xff9a4216.toInt().rgb, 0xff863314.toInt().rgb,
        0xff652510.toInt().rgb,
    )
}
private val hitBlackInviteWhite by lazy {
    setOf(
        0xffffffe7.toInt().rgb, 0xffffffdd.toInt().rgb,
        0xffeeeecf.toInt().rgb,
    )
}
private val hitBlackInviteContent by lazy {
    val output = mutableSetOf<ColorDiffer>()
    output.addAll(hitBlackInviteRed)
    output.addAll(hitBlackInviteWhite)
    output
}

val blackInviteClose by lazy {
    Block("暗影邀请关闭A", phoneScreenWidth / 2 + 260.pt, 75.pt, 40.pt, 40.pt)
}
private val hitBlackInviteCheck by lazy {
    setOf(
        0xffb8a890.toInt().rgb(20),
    )
}
val blackInviteCheck1 by lazy {
    Block("暗影邀请检查1", phoneScreenWidth / 2 - 235.pt, 540.pt, 40.pt, 40.pt)
}
val blackInviteCheck2 by lazy {
    Block("暗影邀请检查2", phoneScreenWidth / 2 + 140.pt, 540.pt, 40.pt, 40.pt)
}
private fun calcBlackGroup(ss: Bitmap?): Block? {
    if (ss == null) return null
    if (Analysis.hit(ss, blackInviteCheck1, hitBlackInviteCheck, 6.pt, 0.5f, "")) {  // 1.0 blackInviteCheck1
        if (Analysis.hit(ss, blackInviteCheck2, hitBlackInviteCheck, 6.pt, 0.5f, "")) { // 1.0 blackInviteCheck2
            if (Analysis.hit(ss, blackInviteClose, hitBlackInviteContent, 6.pt, 0.5f, "")) { //,  0.8316327
                return blackInviteClose
            }
        }
    }
    return null
}

private fun assertPageAtv(ss: Bitmap): Boolean {
    return Analysis.hit(ss, CComponents.activeOkButton, HitColors.hitRedButtonLeftPart1, 6.pt, 0.6f)
}
private fun assertPageEditFriendClose(ss: Bitmap): Boolean {
    //, "closeBtnContent" // minPercent = 0.75206614 左右
    return Analysis.hit(ss, CComponents.checkFriendClose, hitCloseContent, 6.pt, 0.6f) &&
            Analysis.hitAny(ss, CComponents.checkFriendClose, hitCloseWhite, 3.pt)
}
private fun assertPagePopApplyClose(ss: Bitmap): Boolean {
    //, "closeBtnContent" // minPercent = 0.75206614 左右
    return Analysis.hit(ss, CComponents.popApplyClose, hitCloseContent, 6.pt, 0.5f) &&
            Analysis.hitAny(ss, CComponents.popApplyClose, hitCloseWhite, 3.pt)
}

private val hitSceneIcon by lazy {
    setOf(
        0xffA99D73.toInt().rgb(20),
        0xffAEA177.toInt().rgb(20),
    )
}
private fun assertPageSceneRunAgain(ss: Bitmap): Boolean {
    return Analysis.hit(ss, CComponents.popApplyClose, hitCloseContent, 6.pt, 0.5f)
            && Analysis.hitAny(ss, CComponents.popApplyClose, hitCloseWhite, 3.pt)
            // [iconLeft] 0.50617284 hitCount 41 total 81
            && Analysis.hit(ss, CComponents.sceneAgainIconLeft, hitSceneIcon, 5.pt, 0.2f, "") // iconLeft
            // [iconRight] 0.18181819 hitCount 12 total 66
            && Analysis.hit(ss, CComponents.sceneAgainIconRight, hitSceneIcon, 4.pt, 0.1f, "") // iconRight
}

private fun assertPageTalkNpc(ss: Bitmap): Boolean {
    return Analysis.hit(ss, CComponents.sceneNpcChatPart, HitColors.hitSceneSMTalkPart, 18.pt, 0.5f, ) // "hitSceneSMTalkPart"
            || Analysis.hit(ss, CComponents.sceneNpcChatPart2, HitColors.hitSceneSMTalkPart, 18.pt, 0.5f, )
}
private fun assertPageSelectRole(ss: Bitmap): Boolean {
    return Analysis.hit(ss, GComponents.get().selectRole, HitColors.redButton2, 18.pt, 0.42f, )
}
private val hitEnterIcon by lazy {
    setOf(0xffcec5aa.toInt().rgb(10),
        0xffd1c5ad.toInt().rgb(10),)
}
private fun assertPageRootEnter(ss: Bitmap): Boolean {
    // [checkEnterIcon1Book] 0.36734694 hitCount 18 total 49
    // [checkEnterIcon2User] 0.3877551 hitCount 19 total 49
    // [checkEnterIcon3Tip] 0.2857143 hitCount 14 total 49
    return Analysis.hit(ss, CComponents.checkEnterIcon1Book, hitEnterIcon, 6.pt, 0.3f, "") // checkEnterIcon1Book
            && Analysis.hit(ss, CComponents.checkEnterIcon2User,
        hitEnterIcon, 6.pt, 0.3f, "") // checkEnterIcon2User
            && Analysis.hit(ss, CComponents.checkEnterIcon3Tip,
        hitEnterIcon, 6.pt, 0.25f, "")  // , "checkEnterIcon3Tip"
}

private val hitEnterTipBg by lazy {
    setOf(0xff29221a.toInt().rgb(15, 12, 12),)
}
// 公告页面
private fun assertPageRootEnterTipWindow(ss: Bitmap): Boolean {
    return Analysis.hit(ss, CComponents.checkEnterTipLeft, hitEnterTipBg, 8.pt, 0.6f, "") // checkEnterTipLeft
            && Analysis.hit(ss, CComponents.checkEnterTipRight, hitEnterTipBg, 8.pt, 0.6f, "") // checkEnterTipRight
            &&  (Analysis.hit(ss, CComponents.checkEnterTipButton, HitColors.redButton2, 12.pt, 0.44f, ""))  //  "checkEnterTipButton"
}

private val hitYellow by lazy {
    setOf(
        0xffe0c460.toInt().rgb(15),
        0xffd7b64c.toInt().rgb(15),
        0xffe2c03e.toInt().rgb(15),
        0xffcdae4c.toInt().rgb(15),
    )
}
private val hitWhite by lazy {
    setOf(0xfffffefe.toInt().rgb(15),)
}
private val hitFinish by lazy {
    val out = mutableSetOf<ColorDiffer>()
    out.addAll(hitYellow)
    out.addAll(hitWhite)
    out
}
private val castleFinished by lazy { CComponents.castleFinished }
private fun assertGBFWPageFinished(ss: Bitmap): Boolean {
    // [古堡结束] 0.5842027
    // [古堡结束] 0.57600594 hitCount 773 total 1342
    return Analysis.hit(ss, castleFinished, hitFinish, 14.pt, 0.4f) // "古堡结束"
}
private fun assertGBFWPageRevive(ss: Bitmap): Boolean {
    return Analysis.hit(ss, CComponents.reviveInCenter, HitColors.redButton2, 12.pt, 0.46f) &&
            Analysis.hitAny(ss, CComponents.reviveInCenter, HitColors.redButton2WhiteText) &&
            Analysis.hitAny(ss, CComponents.reviveInCenter, HitColors.redButton2Border)
}


private val hitMYBLFinish by lazy {
    val out = mutableSetOf<ColorDiffer>()
    out.addAll(hitWhite)
    out
}
private val myBLFinished by lazy { CComponents.castleFinished }
private fun assertMYBLPageFinished(ss: Bitmap): Boolean {
    //  0.2346041 hitCount 80 total 341
    return Analysis.hit(ss, myBLFinished, hitMYBLFinish, 16.pt, 0.1f) // "古堡结束"
}


private val hitHasNpcAssist by lazy {
    setOf(
        0xff040404.toInt().rgb(6), // 教堂
        0xff080a0d.toInt().rgb(6), // 回音
        0xff0B0503.toInt().rgb(6), // 毁灭.微红
        0xff080a0d.toInt().rgb(6), // 回音

    )
}
//private fun assertHasNpcAssist(ss: Bitmap): Boolean {
//    // [checkHasNpcAssist] 0.9230769 hitCount 108 total 117 // used 就下降到 0.64了
//    return Analysis.hit(ss, GComponents.get().checkHasNpcAssist, hitHasNpcAssist, 5.pt, 0.3f, "checkHasNpcAssist") // "checkHasNpcAssist"
//}
//private fun assertHasNpcAssistUsed(ss: Bitmap): Boolean {
//    // [checkHasNpcAssist] 0.6495727 hitCount 76 total 117
//    // [checkHasNpcAssist] 0.9059829 hitCount 106 total 117
//    return Analysis.hit(ss, GComponents.get().checkHasNpcAssistUsed, hitHasNpcAssist, 5.pt, 0.3f, "checkHasNpcAssistUsed" ) // "checkHasNpcAssistUsed"
//}
private fun calcNpcAssist(ss: Bitmap?): Block? {
    if (ss == null) return null
    if (Properties.scene == C.S_RD) {
        // 随机本尝试强制按钮区域
        return Block("npcAssist随机兜底", GComponents.get().selfHp.left + 620.pt, 790.pt, 30.pt, 20.pt)
    }

    val is720P = phoneDefaultDisplayPoint.x == 1280 && phoneDefaultDisplayPoint.y == 720 && phoneDensityDpi == 320
    if (is720P) {
        // 这里用固定默认值兜底
        Block("npcAssist720", GComponents.get().selfHp.left + 620.pt, 790.pt, 30.pt, 20.pt)
//         if (Properties.scene == C.S_JL) return Block("npcAssist720激流", GComponents.get().selfHp.left + 620.pt, 790.pt, 30.pt, 20.pt)
//         else if (Properties.scene == C.S_SM) return Block("npcAssist720神庙", GComponents.get().selfHp.left + 620.pt, 790.pt, 30.pt, 20.pt)
//         else if (Properties.scene == C.S_HM) return Block("npcAssist720毁灭", GComponents.get().selfHp.left + 620.pt, 790.pt, 30.pt, 20.pt)
//         else if (Properties.scene == C.S_FW) return Block("npcAssist720疯王", GComponents.get().selfHp.left + 620.pt, 790.pt, 30.pt, 20.pt)
//         else if (Properties.scene == C.S_HY) return null
//         else if (Properties.scene == C.S_JT) return null
//         else if (Properties.scene == C.S_RH) return null
//         else if (Properties.scene == C.S_FB) return Block("npcAssist720风暴", GComponents.get().selfHp.left + 620.pt, 790.pt, 30.pt, 20.pt)
//         else if (Properties.scene == C.S_YW) return null
//         else if (Properties.scene == C.S_FM) return null
//         else if (Properties.scene == C.S_MY) return null
    }

    var x = phoneScreenWidth / 2 - 100.pt
    val left = GComponents.get().selfHp.left + 620.pt
    val y = 800.pt
    while (x > left) {
        if (hitHasNpcAssist.containsSimilarColor(App.take().takePixel(ss, x, y))) {
            val block = Block("npcAssist", max(left, x - 40.pt), y - 10.pt, 40.pt, 20.pt)
            if (Analysis.hit(ss, block, hitHasNpcAssist, 6.pt, 0.5f, ) // hitHasNpcAssist
                ) {
                return block
            }
        }
        x -= 10.pt
    }

    return null
}

val fsInfo: String get() {
    val sb = StringBuffer()
    sb.append("[${(System.currentTimeMillis() - xLastSceneEnterTs) / 1000}]")

    if (bsPage == PAGE_MAIN) sb.append(SCRIPT_MAIN)
    else if (bsPage == PAGE_SKIP) sb.append("[动画]")
    else if (bsPage == PAGE_BASE_MENU) sb.append("[主菜单]")
    else if (bsPage == PAGE_REVIVE) sb.append("[被击败]")
    else if (bsPage == PAGE_TALK_BLACKSMITH) sb.append("[对话铁匠]")
    else if (bsPage == PAGE_SCENE_COMPLETED) sb.append("[再次挑战]")
    else if (bsPage == PAGE_SCENE_ENTER) sb.append("[副本入口${if (npcAssistBlock != null) ":可援助" else "-"}]")
    else if (bsPage == PAGE_CLOSEABLE) sb.append("[可关闭]")
    else if (bsPage == PAGE_BE_INVITED_BLACK_GROUP) sb.append("[暗影]")
    else if (bsPage == PAGE_ATV) sb.append("[融魂跳崖误判活动]")
    else if (bsPage == PAGE_SCENE_GROUP_INVITE_PLAYER) sb.append("[邀请总览]")
    else if (bsPage == PAGE_TALK_NPC) sb.append("[对话NPC]")
    else if (bsPage == PAGE_EDIT_FRIEND_NAME) sb.append("[编辑好友备注]")
    else if (bsPage == PAGE_BE_INVITED && bsAcceptInvite) sb.append("[被邀请:同意]")
    else if (bsPage == PAGE_BE_INVITED) sb.append("[被邀请]")
    else if (bsPage == PAGE_APPLY && acceptApply) sb.append("[申请页:同意]")
    else if (bsPage == PAGE_APPLY) sb.append("[申请页]")
    else if (bsPage == PAGE_APPLY_EMPTY) sb.append("[申请页:空]")
    else if (bsPage == PAGE_BOSS_DIRECT_OR_BUFF) sb.append("[直通orBuff]")
    else if (bsPage == PAGE_REVIVE_BY_OTHER) sb.append("[接受复活弹窗]")
    else if (bsPage == PAGE_ROOT_ENTER) sb.append("[登录根页面]")
    else if (bsPage == PAGE_ROOT_ENTER_TIP) sb.append("[登录公告]")
    else if (bsPage == PAGE_SCENE_ENTER_IMM) sb.append("[不朽秘境入口]")
    else if (bsPage == PAGE_SCENE_ENTER_TZ) sb.append("[挑战秘境入口]")
    else if (bsPage == PAGE_MYMZ_START) sb.append("[魔域迷踪开始]")
    else if (bsPage == PAGE_MYBL_FINISH) sb.append("[魔域堡垒结束]")
    else if (bsPage == PAGE_MYBL_FAIL_EXIT) sb.append("[堡垒被击败]")
    else if (bsPage == PAGE_LX_REVIVE) sb.append("[恐惧裂隙被击败]")
    else if (bsPage == PAGE_LX_USE_CONFIRM) sb.append("[恐惧裂隙开启弹窗]")
    else if (bsPage == PAGE_IMM_KEY) sb.append("[秘境钥匙页面]")
    else if (bsPage == PAGE_SELECT_ROLE) sb.append("[选择角色]")
    else if (bsPage == PAGE_SCENE_GROUP_INVITE_PLAYER) sb.append("[邀请总览]")
    else if (bsPage == PAGE_POP_TEXT) sb.append("[弹窗中]")

    if (bsHasInteract) sb.append("[交互]")
    if (bsHasInteract && bsHasTransmitting) sb.append("[互动]")
    else if (bsHasTransmitting)  sb.append("[读条]")

    if (blockEliteEnemyBoss != null) sb.append("[😈]")
    else if (bsHasEnemy) sb.append("[e:$bsEnemy]")

    if (blockBlueDoor != null) sb.append("[传送门]")
    if (blockBlacksmith != null) sb.append("[铁匠]")
    if (hasApplyDot) sb.append("[申请]")
    if (playerHpLosing) sb.append("[掉血]")
    if (hasBackpack100) sb.append("[背包已满]")
    if (myBLLv3) sb.append("[宝箱撤退]")
    if (lxInProgress) sb.append("[裂隙精华收集中]")

    return sb.toString()
}


private val hitImmEnterRed by lazy {
    setOf(
        0xff6d2b15.toInt().rgb(10), 0xff7a3015.toInt().rgb(10),
        0xff9a4216.toInt().rgb(10), 0xff863314.toInt().rgb(10),
        0xff652510.toInt().rgb(10),
    )
}
private val hitImmEnterWhite by lazy {
    setOf(0xfffefdd1.toInt().rgb, 0xfff0e5bb.toInt().rgb,)
}
private fun calcPageImmEnter(ss: Bitmap): Block? {
    val se = GComponents.get().buttonCheckPart2Imm
    // 0.18528996 hitCount 262 total 1414
    //  0.20634921 hitCount 13 total 63
    val hit =  Analysis.hit(ss, se, hitImmEnterRed, 15.pt, 0.16f, )
            && Analysis.hitAny(ss, se, hitImmEnterWhite, 2.pt, )
    if (hit) return se

    val step = 8.pt
    val buttonCheckPart = Block("按钮检测",  phoneScreenWidth - 500.pt, se.top, 420.pt, 30.pt)
    var x = buttonCheckPart.right - 1
    while (x > buttonCheckPart.left) {
        var y = buttonCheckPart.top
        while (y < buttonCheckPart.bottom) {
            if (hitImmEnterRed.containsSimilarColor(App.take().takePixel(ss, x, y))) {
                // -300 是少算一些不需要计算的吧
                val out = Block("enter", x - 300.pt, buttonCheckPart.top ,60.pt, 40.pt)
                if (Analysis.hit(ss, se, HitColors.hitSceneButton, 2.pt, 0.54f)) {
                    return out // sceneEnter  // , "sceneEnter" 0.75 0.83 0.92
                }
            }
            y += step
        }
        x -= step
    }
    return null
}

private fun calcPageTzEnter(ss: Bitmap): Block? {
    val se = GComponents.get().buttonCheckPart2Tz
    // 0.18528996 hitCount 262 total 1414
    //  0.20634921 hitCount 13 total 63
    val hit =  Analysis.hit(ss, se, hitImmEnterRed, 15.pt, 0.16f, )
            && Analysis.hitAny(ss, se, hitImmEnterWhite, 2.pt, )
    if (hit) return se

    val step = 8.pt
    val buttonCheckPart = Block("按钮检测",  phoneScreenWidth - 500.pt, se.top, 420.pt, 30.pt)
    var x = buttonCheckPart.right - 1
    while (x > buttonCheckPart.left) {
        var y = buttonCheckPart.top
        while (y < buttonCheckPart.bottom) {
            if (hitImmEnterRed.containsSimilarColor(App.take().takePixel(ss, x, y))) {
                // -300 是少算一些不需要计算的吧
                val out = Block("enter", x - 300.pt, buttonCheckPart.top ,60.pt, 40.pt)
                if (Analysis.hit(ss, se, HitColors.hitSceneButton, 2.pt, 0.54f)) {
                    return out // sceneEnter  // , "sceneEnter" 0.75 0.83 0.92
                }
            }
            y += step
        }
        x -= step
    }
    return null
}

private fun assertPageImmBuffWord(ss: Bitmap): Boolean {
    return Analysis.hit(ss, GComponents.get().immWordCenterButton, HitColors.redButton2, 10.pt, 0.48f)
            && Analysis.hitAny(ss, GComponents.get().immWordCenterButton, HitColors.redButton2Border)
            && Analysis.hitAny(ss, GComponents.get().immWordCenterButton, HitColors.redButton2WhiteText)
}
private fun assertPageDailySign(ss: Bitmap): Boolean {
    return Analysis.hit(ss, GComponents.get().immDailyButton, HitColors.redButton2, 10.pt, 0.48f)
            && Analysis.hitAny(ss, GComponents.get().immDailyButton, HitColors.redButton2Border)
            && Analysis.hitAny(ss, GComponents.get().immDailyButton, HitColors.redButton2WhiteText)
}
private val hitImmKeyBG by lazy { setOf(0xFF0E0E12.toInt().rgb(6),) }
private fun assertPageImmKey(ss: Bitmap): Boolean {
    // hit [immKey1] 0.6532258 hitCount 81 total 124
    // hit [immKey2] 0.7177419 hitCount 89 total 124
    return Analysis.hit(ss, GComponents.get().immKeyA, hitImmKeyBG, 10.pt, 0.45f ) //immKey1
            && Analysis.hit(ss, GComponents.get().immKeyB, hitImmKeyBG, 10.pt, 0.45f ) // immKey2
}

private val backPackTip by lazy {
    setOf(
        0xff2d150c.toInt().rgb(15), //
        0xff572a0b.toInt().rgb(15), //
        0xffa69873.toInt().rgb(15), //text
        0xffa39570.toInt().rgb(15), //text
    )
}

private val checkRect by lazy {
    Block("检查提示", phoneScreenWidth/2 - 100.pt, phoneScreenHeight/2 - 20.pt, 200.pt, 40.pt)
}

private fun assertBackpack100(ss: Bitmap?): Boolean {
    if(ss == null) return false
    // 看背包是否满了
    // info hit [invokeBackpack100] 0.6666667
    return Analysis.hit(ss, checkRect, backPackTip, 12.pt, 0.5f, "") // invokeBackpack100
}


private fun assertLXPageUseConfirm(ss: Bitmap?): Boolean {
    if (ss == null) return false
    return Analysis.hit(ss, CComponents.lxUseConfirmClose, hitCloseContent, 6.pt, 0.6f) &&
            Analysis.hitAny(ss, CComponents.lxUseConfirmClose, hitCloseWhite, 3.pt)
}
private val hitLxBtnDisable by lazy {
    setOf(
        0xff2a2a2a.toInt().rgb(16)
    )
}
private fun assertLXConfirmLack(ss: Bitmap?): Boolean {
    if (ss == null) return false
    // hit [lxUseConfirmBtnDisable] 1.0 hitCount 196 total 196
    return Analysis.hit(ss, CComponents.lxUseConfirmBtn, hitLxBtnDisable, 5.pt, 0.6f) // lxUseConfirmBtnDisable
}


private val hitBackBoxCountNum by lazy {
    setOf(
        0xFFF9EEB9.toInt().rgb(2), // 白色数字
        0xFFF0E6B3.toInt().rgb(2), // 白色数字
    )
}
val tsNumBox by lazy {
    Block("宝箱数字", GComponents.get().selfHp.left + 42.pt, 490.pt, 8.pt, 20.pt)
}
private val hitBoxLvRed by lazy {
    setOf(
        0xFFC62F40.toInt().rgb(4),
        0xFFD13043.toInt().rgb(4),
        0xFFE83348.toInt().rgb(4),
        0xFFF0344A.toInt().rgb(4),
        0xFFF6354B.toInt().rgb(4),
        0xFFF7354C.toInt().rgb(4),
        0xFFF9364C.toInt().rgb(4),
        0xFFFA364C.toInt().rgb(4),
        0xFFFA364D.toInt().rgb(4),
        0xFFFB364D.toInt().rgb(4),
    )
}
private val tsNumLvA by lazy {
    Block("等级数字", GComponents.get().selfHp.right - 10.pt, 322.pt, 12.pt, 22.pt)
}

val myblNumLvPart by lazy {
    Block("等级数字", GComponents.get().selfHp.right - 20.pt, 320.pt, 32.pt, 24.pt)
}

private fun assertMYBLStateLV3(ss: Bitmap?): Boolean {
    if (ss == null) return false
    if ((phoneDefaultDisplayPoint.x == 1280 && phoneDefaultDisplayPoint.y == 720 && phoneDensityDpi == 320)
        || phoneDefaultDisplayPoint.x == 720 && phoneDefaultDisplayPoint.y == 1280 && phoneDensityDpi == 320) {
        val step = 1
        var x = myblNumLvPart.right - 1
        var hitCount = 0
        while (x > myblNumLvPart.left) {
            var y = myblNumLvPart.top
            while (y < myblNumLvPart.bottom) {
                if (hitBoxLvRed.containsSimilarColor(App.take().takePixel(ss, x, y))) {
                    hitCount++
                }
                y += step
            }
            x -= step
        }
        // 超清分辨率 画面品质中
        // 数字1 -> ?
        // 数字2 -> 21 22 25
        // 数字3 -> 30 31
        return hitCount > 30
    } else {
        //
        //lv1 0.21 [hitBoxLv] 0.21875 hitCount 7 total 32
        //lv2 0.18 [hitBoxLv] 0.1875 hitCount 6 total 32
        //lv3 0.21 [hitBoxLv] 0.21875 hitCount 7 total 32
        // hit [hitBoxLv] 0.21875 hitCount 7 total 32
        // hit [hitBackNum] 0.33333334 hitCount 7 total 21  小米手机
        return  Analysis.hit(ss, tsNumLvA, hitBoxLvRed, 3.pt, 0.2f, ) // 1或者3 , "hitBoxLv"
                && Analysis.hit(ss, tsNumBox, hitBackBoxCountNum, 3.pt, 0.3f, )  // 10位数 , "hitBackNum"
    }
}

private val hitLxEnableCount by lazy {
    setOf(
        0xFFB39072.toInt().rgb(16, 10, 12),
    )
}
//private fun assertLXCountEnable(ss: Bitmap?): Boolean {
//    if (ss == null) return false
//    // 数字0  0.18181819 hitCount 32 total 176
//    // 数字1  0.09659091 hitCount 17 total 176
//    val p = Analysis.hitP(ss, GComponents.get().infoLxEnableCount, hitLxEnableCount, 2.pt, "infoLxEnableCount") // 1或者3 , "infoLxEnableCount"
//    return p in 0.06 .. 0.12
//}

val blockLxInScene by lazy {
    Block("裂隙副本中", GComponents.get().selfHp.left + 60.pt, 240.pt, 120.pt, 30.pt)
}
val hitLxNameRed by lazy {
    setOf(
        0xFFC23636.toInt().rgb(20, 10, 10),
    )
}
private fun assertRedStateTitle(ss: Bitmap?): Boolean {
    if (ss == null) return false
    // analysisInfo hit [blockLxInScene] 0.16701902 hitCount 79 total 473
    return Analysis.hit(ss, blockLxInScene, hitLxNameRed, 4.pt, 0.1f)// blockLxInScene
}
private val hitLxInProgress by lazy {
    setOf(
        0xff0C0807.toInt().rgb(6), // 黑底
        0xffFAB06A.toInt().rgb(6), // 橙字
        0xff802013.toInt().rgb(38, 20, 20), // 红 不准？
    )
}
private fun assertLxInProgress(ss: Bitmap?): Boolean {
    if (ss == null) return false
    return Analysis.hit(ss, GComponents.get().lxProgress1, hitLxInProgress, 8.pt, 0.3f, "")
            || Analysis.hit(ss, GComponents.get().lxProgress2, hitLxInProgress, 8.pt, 0.3f, "") // 不对的时候只有 0.02
}

private val hitMonsterEssence by lazy {
    setOf(
        0xffd5d5b4.toInt().rgb,
        0xffc5c5a7.toInt().rgb,
        0xffb3b39d.toInt().rgb,
    )
}
private fun calcMonsterEssence(ss: Bitmap?): Block? {
    if (ss == null) return null
    val mapEssenceCheckPart = GComponents.get().mapEssenceCheckPart
    var y = mapEssenceCheckPart.bottom
    while (y > mapEssenceCheckPart.top) {
        var x = mapEssenceCheckPart.left
        while (x < mapEssenceCheckPart.right) {
            val c = App.take().takePixel(ss, x, y)
            if (hitMonsterEssence.containsSimilarColor(c)) {
                val block = Block("祭坛图标", mapEssenceCheckPart.left, y, 20.pt, 20.pt)
                if (Analysis.hit(ss, block, hitMonsterEssence, 2, 0.28f)) { // , , "祭坛图标"
                    return block
                }
            }
            x += 2.pt
        }
        y -= 2.pt
    }
    return null
}









private var lastCheckTs = 0L
private fun check() {
    if (System.currentTimeMillis() - lastCheckTs > 2845678
        && runTime > 10000) {
        lastCheckTs = System.currentTimeMillis()
        appScopeLaunch(Dispatchers.Main) {
            val cdKeyInfo = Keys.usingCDKeyInfo
            val deviceOk = cdKeyInfo?.deviceId == appDeviceId || cdKeyInfo?.deviceId == appDeviceId.substring(0, min(30, appDeviceId.length))
            if (!deviceOk) Keys.clear()

            val isUsingCDKeyInfoActivating = cdKeyInfo != null &&
                    deviceOk &&
                    cdKeyInfo.expiredTimestamp > System.currentTimeMillis() - 43200000
            if (!isUsingCDKeyInfoActivating) {
                Controller.get().stop("")
                analysisEvent("G3crackedAX")
            }
        }
    }
}